Skip to content

feat: support jq in response filter#7424

Open
K3UL wants to merge 5 commits intousebruno:mainfrom
K3UL:feature/support-jq
Open

feat: support jq in response filter#7424
K3UL wants to merge 5 commits intousebruno:mainfrom
K3UL:feature/support-jq

Conversation

@K3UL
Copy link
Contributor

@K3UL K3UL commented Mar 10, 2026

Description

Closes #7423

This PR introduces the ability to filter JSON response either using jq or JSONPath (JSONPath being the existing behavior). jq offers much more powerful capabilities to manipulate JSON and is a great tool to quickly analyze json responses.

The feature presents a selector next to the filter, and the field adapts to the choice. The jq filtering behaves the same as the JSONPath (dynamic while typing, same field).

JSONPath stays the default selection.

image

To my knowledge, no serious competitor has this feature (except RapidAPI/Paw, and it is MacOS-only). In open-source, there is a long standing issue for Insomnia to get this but it is not going anywhere so far.

I think this feature would represent a real competitive advantage, on top of being a really nice-to-have (I implemented it because I have longed for it in all the tools I have used so far).


Without filter image

Proper error handling image

This feature leverages jq-wasm, which is a webassembly build of jq that requires no extra native dependency, and is the library used by the official jq playground.

About wasm-unsafe-eval

To be able to use this webassembly library, the Content Security Policy has to be adjusted, with the wasm-unsafe-eval directive enabled.
Despite its name, this policy is narrowly scoped, and is actually covered by tests in the Electron project itself. It is also less permissive than several policies already in place in the project.
This policy only affects WebAssembly compilation.

About the added dependency

The gzip size of the bundle increases by ~650kB, which is around 10% of the bundle, but marginal considering the Electron footprint.

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Summary by CodeRabbit

  • New Features

    • Toggleable response filtering: switch between JSONPath and jq modes.
    • jq-based filtering execution with results shown inline and informative placeholders/infotips.
  • Bug Fixes

    • Shows clear error indicator when a filter fails.
  • Style

    • UI for filter-type toggle and error display styling improved.
  • Chores

    • Enabled WebAssembly support and added jq runtime dependency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

Walkthrough

Adds jq-based JSON filtering: new jq-wasm dependency, a runJqFilter utility, CSP update to permit WASM, and UI+logic to toggle and run jq vs JSONPath filters in the QueryResult components.

Changes

Cohort / File(s) Summary
Dependency
packages/bruno-app/package.json
Added jq-wasm dependency (1.1.0-jq-1.8.1).
jq runtime service
packages/bruno-app/src/utils/common/jq-service.js
New runJqFilter(data, filter) async wrapper around jq.raw(...) that validates exitCode, returns stdout, and throws on error.
CSP update
packages/bruno-electron/src/index.js
Expanded Content-Security-Policy script-src to include 'wasm-unsafe-eval' to allow WASM execution.
Filter UI & styling
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js, packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
Added filter-type toggle (JSONPath ↔ jq), new props (filterType, onFilterTypeChange, jqError), conditional placeholder/infotip and jqError indicator; new styles for toggle and .jq-error.
Core QueryResult logic
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
Introduced filterType, jqResult, jqError state; useEffect to run runJqFilter when filterType === 'jq'; consume jqResult when rendering; pass new props to QueryResultFilter.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Filter as QueryResultFilter
    participant QR as QueryResult
    participant Service as jqService
    participant Wasm as jqWasm

    User->>Filter: Toggle filter type / enter filter
    Filter->>QR: onFilterTypeChange(type) / onChange(filter)
    QR->>QR: set state (filterType, clear filter, etc.)

    alt jq mode
        QR->>Service: runJqFilter(data, filter)
        Service->>Wasm: jq.raw(data, filter)
        Wasm-->>Service: {stdout, exitCode, stderr}
        Service-->>QR: stdout (or throw error)
        QR->>QR: set jqResult / jqError
        QR->>User: render jqResult or show jqError
    else jsonpath mode
        QR->>QR: formatResponse with JSONPath
        QR->>User: render JSONPath-filtered output
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • #6139: Modifies electron CSP script-src—overlaps with the CSP change here.
  • #6305: Also updates CSP in packages/bruno-electron/src/index.js; coordinate for consistent policy.

Suggested reviewers

  • helloanoop
  • bijin-bruno
  • lohit-bruno
  • naman-bruno

Poem

A toggle flips from path to jq,
WASM hums beneath the UI,
Filters run, errors show their face,
JSON sings in a smaller space,
Small change, bright new query sky.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implementation fully addresses #7423 objectives: jq filtering is integrated as an alternative to JSONPath, uses a selector UI, maintains dynamic filtering behavior, and leverages jq-wasm for functionality.
Out of Scope Changes check ✅ Passed All changes are directly scoped to jq integration: dependency addition (jq-wasm), service layer (jq-service.js), component enhancements (QueryResultFilter, QueryResult), styling (StyledWrapper), and CSP adjustment for WebAssembly. No extraneous modifications detected.
Title check ✅ Passed The title clearly and concisely summarizes the main feature addition: jq support in the response filter component.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)

160-163: jq output may lack pretty-printing for CodeEditor display.

When filterType === 'jq', formattedData uses jqResult directly (line 162). Per the relevant code snippets, jqResult is raw stdout from jq-wasm without the pretty-printing that formatResponse applies via fastJsonFormat().

This could result in compact, hard-to-read JSON in the editor. Consider parsing and re-formatting the jq output:

♻️ Proposed fix
+import { safeStringifyJSON, safeParseJSON } from 'utils/common';
+
 // In formattedData useMemo:
       if (filterType === 'jq' && filter) {
-        return jqResult ?? formatResponse(data, dataBuffer, selectedFormat, null);
+        if (jqResult) {
+          // Pretty-print the jq result
+          const parsed = safeParseJSON(jqResult);
+          return parsed !== undefined ? safeStringifyJSON(parsed, true) : jqResult;
+        }
+        return formatResponse(data, dataBuffer, selectedFormat, null);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/ResponsePane/QueryResult/index.js` around
lines 160 - 163, When filterType === 'jq' the code returns jqResult raw
(jqResult) which is unformatted stdout from jq-wasm; instead parse and
pretty-print it before returning so the CodeEditor shows formatted JSON. Update
the branch that currently returns jqResult ?? formatResponse(...) to: if
jqResult is present, attempt to JSON.parse(jqResult) and pass the parsed value
through formatResponse (or fastJsonFormat via formatResponse) to produce
pretty-printed output, falling back to the original jqResult string if
parsing/formatting fails; ensure you reference the existing symbols filterType,
jqResult, formatResponse (and fastJsonFormat used inside it) when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-app/src/components/ResponsePane/QueryResult/index.js`:
- Around line 146-150: The component captures jq errors via setJqError but never
surfaces them in the UI; modify the QueryResult render to display jqError when
present (similar to how the main error prop is rendered) so users know their jq
filter failed. Locate the QueryResult component state variables (jqError,
jqResult) and the catch block that calls setJqError, then add a conditional
branch in the render output that shows jqError (with appropriate
styling/placement used for error) instead of falling back to the unfiltered data
when jqError is non-empty.

---

Nitpick comments:
In `@packages/bruno-app/src/components/ResponsePane/QueryResult/index.js`:
- Around line 160-163: When filterType === 'jq' the code returns jqResult raw
(jqResult) which is unformatted stdout from jq-wasm; instead parse and
pretty-print it before returning so the CodeEditor shows formatted JSON. Update
the branch that currently returns jqResult ?? formatResponse(...) to: if
jqResult is present, attempt to JSON.parse(jqResult) and pass the parsed value
through formatResponse (or fastJsonFormat via formatResponse) to produce
pretty-printed output, falling back to the original jqResult string if
parsing/formatting fails; ensure you reference the existing symbols filterType,
jqResult, formatResponse (and fastJsonFormat used inside it) when making the
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0af40d12-a122-49b6-b3bc-57b7b2f763bd

📥 Commits

Reviewing files that changed from the base of the PR and between f123a2b and 85b0dff.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • packages/bruno-app/package.json
  • packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js
  • packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
  • packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
  • packages/bruno-app/src/utils/common/jq-service.js
  • packages/bruno-electron/src/index.js

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)

162-165: Brief flicker during jq filter computation.

When jqResult is null (lines 139-140 clear it), the fallback shows unfiltered data momentarily. This is acceptable UX, but if users find it jarring, consider preserving the previous result until the new one arrives or showing a subtle loading indicator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/ResponsePane/QueryResult/index.js` around
lines 162 - 165, The fallback returns unfiltered data when jqResult is null
causing a flicker; change the logic so the UI preserves the previous rendered
result (e.g., keep a cached "lastJqResult") or shows a subtle loading state
instead of immediately calling formatResponse(data,...). Update the branch that
checks filterType === 'jq' && filter to return lastJqResult if jqResult is null
(or a loading flag) and only replace lastJqResult when jqResult becomes
non-null; use the existing symbols jqResult, filterType, filter, formatResponse,
data, dataBuffer, and selectedFormat to find and update the code paths that
clear and render jq results.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js (1)

61-76: Consider adding accessible labels to toggle buttons.

The toggle buttons lack aria-label or aria-pressed attributes for screen reader users. This is a minor accessibility enhancement.

♿ Suggested accessibility improvement
           <button
-            className={`toggle-btn ${filterType === 'jsonpath' ? 'active' : ''}`}
+            className={`toggle-btn ${filterType === 'jsonpath' ? 'active' : ''}`}
+            aria-pressed={filterType === 'jsonpath'}
+            aria-label="Filter with JSONPath"
             onClick={() => handleFilterTypeChange('jsonpath')}
           >
             JSONPath
           </button>
           <button
-            className={`toggle-btn ${filterType === 'jq' ? 'active' : ''}`}
+            className={`toggle-btn ${filterType === 'jq' ? 'active' : ''}`}
+            aria-pressed={filterType === 'jq'}
+            aria-label="Filter with jq"
             onClick={() => handleFilterTypeChange('jq')}
           >
             jq
           </button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js`
around lines 61 - 76, The toggle buttons for switching filter types lack
accessible labeling—update the two buttons in QueryResultFilter so each includes
an aria-label (e.g., "Select JSONPath filter" and "Select jq filter") and an
aria-pressed attribute tied to the current filterType state (use
aria-pressed={filterType === 'jsonpath'} for the JSONPath button and
aria-pressed={filterType === 'jq'} for the jq button); keep the existing onClick
handlers (handleFilterTypeChange) and class logic intact so the visual and
programmatic state remain consistent with isExpanded and mode checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-app/src/components/ResponsePane/QueryResult/index.js`:
- Line 141: The call to runJqFilter is passing data (which may be a parsed
object) into jq.raw(), causing "[object Object]" errors; update the call site
using runJqFilter(data, filter) to pass a proper JSON string when data is an
object (e.g., JSON.stringify(data)) or pass the original dataBuffer instead so
jq.raw() always receives a raw JSON string; locate usages of runJqFilter and
jq.raw() and ensure either JSON.stringify(data) is used or dataBuffer is
forwarded.

---

Nitpick comments:
In `@packages/bruno-app/src/components/ResponsePane/QueryResult/index.js`:
- Around line 162-165: The fallback returns unfiltered data when jqResult is
null causing a flicker; change the logic so the UI preserves the previous
rendered result (e.g., keep a cached "lastJqResult") or shows a subtle loading
state instead of immediately calling formatResponse(data,...). Update the branch
that checks filterType === 'jq' && filter to return lastJqResult if jqResult is
null (or a loading flag) and only replace lastJqResult when jqResult becomes
non-null; use the existing symbols jqResult, filterType, filter, formatResponse,
data, dataBuffer, and selectedFormat to find and update the code paths that
clear and render jq results.

In
`@packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js`:
- Around line 61-76: The toggle buttons for switching filter types lack
accessible labeling—update the two buttons in QueryResultFilter so each includes
an aria-label (e.g., "Select JSONPath filter" and "Select jq filter") and an
aria-pressed attribute tied to the current filterType state (use
aria-pressed={filterType === 'jsonpath'} for the JSONPath button and
aria-pressed={filterType === 'jq'} for the jq button); keep the existing onClick
handlers (handleFilterTypeChange) and class logic intact so the visual and
programmatic state remain consistent with isExpanded and mode checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 006172e0-9a8f-48eb-b913-c3d6004ede45

📥 Commits

Reviewing files that changed from the base of the PR and between 85b0dff and a237563.

📒 Files selected for processing (4)
  • packages/bruno-app/package.json
  • packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js
  • packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
  • packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js (1)

63-74: Consider adding type="button" to prevent unintended form submissions.

While these buttons aren't inside a form currently, explicitly setting type="button" is a defensive practice that prevents accidental form submission if the component is ever nested inside a form element.

💡 Proposed fix
          <button
+           type="button"
            className={`toggle-btn ${filterType === 'jsonpath' ? 'active' : ''}`}
            onClick={() => handleFilterTypeChange('jsonpath')}
          >
            JSONPath
          </button>
          <button
+           type="button"
            className={`toggle-btn ${filterType === 'jq' ? 'active' : ''}`}
            onClick={() => handleFilterTypeChange('jq')}
          >
            jq
          </button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js`
around lines 63 - 74, The toggle buttons for selecting filter type (the JSX
buttons that use filterType and onClick={() => handleFilterTypeChange(...)})
should explicitly include type="button" to prevent accidental form submission if
this component is later nested inside a form; update both the JSONPath and jq
<button> elements to add type="button" while leaving their className and onClick
handlers unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js`:
- Around line 63-74: The toggle buttons for selecting filter type (the JSX
buttons that use filterType and onClick={() => handleFilterTypeChange(...)})
should explicitly include type="button" to prevent accidental form submission if
this component is later nested inside a form; update both the JSONPath and jq
<button> elements to add type="button" while leaving their className and onClick
handlers unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9dab5fe3-c81b-4fe5-957b-3b0e1e6e9b5c

📥 Commits

Reviewing files that changed from the base of the PR and between a237563 and cf21f6d.

📒 Files selected for processing (1)
  • packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js

@K3UL K3UL changed the title Support JQ in response filter feat: support jq in response filter Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support jq filtering for JSON responses

1 participant